package movee.api.aspect;

import movee.api.utils.ApiUtils;
import movee.domain.api.ApiConstants;
import movee.domain.api.ExceptionResponse;
import movee.domain.exception.AppStatusCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;

import jakarta.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

/**
 * controller异常处理
 *
 * @author movee
 */
@Slf4j
public class BasicApiExceptionHandler {

    private final HttpServletRequest httpRequest;

    public BasicApiExceptionHandler(HttpServletRequest httpRequest) {
        this.httpRequest = httpRequest;
    }

    /**
     * 异常响应消息体
     * @param statusCode statusCode
     * @param message message
     * @param errors errors
     * @return
     */
    public ExceptionResponse buildExceptionResponseBody(AppStatusCode statusCode,
                                                      String message,
                                                      ExceptionResponse.Errors errors) {
        ExceptionResponse resp = new ExceptionResponse();

        // 从请求中获取requestId，否则生成一个
        String requestId = (String) httpRequest.getAttribute(ApiConstants.X_API_REQUEST_ID);
        if (!StringUtils.hasLength(requestId)) {
            requestId = UUID.randomUUID().toString();
        }

        // 根据不同异常生成相应的http响应
        resp.setRequestId(requestId);
        resp.setCode(statusCode.getCode());
        resp.setMessage(message);
        resp.setErrors(errors);
        return resp;
    }

    /**
     * 异常响应
     * @param statusCode statusCode
     * @param message message
     * @param errors errors
     * @return
     */
    public ResponseEntity<ExceptionResponse> buildExceptionResponseEntity(AppStatusCode statusCode,
                                                                          String message,
                                                                          ExceptionResponse.Errors errors) {

        ExceptionResponse resp = buildExceptionResponseBody(statusCode, message, errors);

        // 构建http响应
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.set(ApiConstants.X_API_REQUEST_ID, resp.getRequestId());
        String date = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME);
        httpHeaders.set(ApiConstants.X_API_DATE, date);
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        ResponseEntity<ExceptionResponse> responseEntity;
        responseEntity = new ResponseEntity<>(resp, httpHeaders, statusCode.getHttpStatus());

        return responseEntity;
    }

    /**
     * 打印异常信息
     * @param t 异常
     */
    public void logException(Throwable t) {
        log.debug("{}", ExceptionUtils.getStackTrace(t));

        String requestRealIp = ApiUtils.getClientRealIp(httpRequest);
        String requestEntry = (String) httpRequest.getAttribute(ApiConstants.API_REQUEST_ENTRY);
        if (!StringUtils.hasLength(requestEntry) && t instanceof BindException) {
            requestEntry = ((BindException) t).getObjectName();
        }

        // 打印http请求处理时间
        Instant start = (Instant) httpRequest.getAttribute(ApiConstants.API_REQUEST_START);
        if (start == null) {
            log.info("clientRealIp: {}, finish {}", requestRealIp, requestEntry);
        } else {
            Duration duration = Duration.between(start, Instant.now());
            log.info("clientRealIp: {}, finish {}, duration time: {} ms",
                    requestRealIp, requestEntry, duration.toMillis());
        }
    }

}
